
#include <Socket.hh>
#include <stdexcept>
#include <fcntl.h>
#include <string.h>

#define BAD_SOCKET

Socket::Socket( int domain, int type, int protocol ) 
    : fd_( socket( domain, type, protocol ) )
{
    if ( -1 == fd_ ) {
        throw std::runtime_error("socket()");
    }
}

Socket::Socket( Socket::socket_t fd ) 
    : fd_ (fd )
{
    if ( -1 == fd_ ) {
        throw std::runtime_error("socket()");
    }
}

Socket::~Socket() {
    if ( -1 != fd_ ) close( fd_ );
}
    
int Socket::shutdown( int how ) {
    return ::shutdown( fd_, how );
}

int Socket::bind( Address const &addr ) {
    return ::bind( fd_, addr.sockaddr() , addr.len() );
}

int Socket::listen( int backlog ) {
    return ::listen( fd_, backlog );
}

Socket Socket::accept( Address &from ) {
    struct sockaddr_storage storage;
    socklen_t len(sizeof(storage));
    int fd( ::accept( fd_, (struct sockaddr *)&storage, &len ));
    from = Address::Factory( storage );
    return Socket(fd);
}

int Socket::set_nonblock() {
	int flags;
    if ( -1 == ( flags = ::fcntl(fd_, F_GETFL, 0))) {
		throw std::runtime_error("fcntl(F_GETFL)");
	}
	return ::fcntl(fd_, F_SETFL, flags | O_NONBLOCK);
}

socklen_t Socket::Address::len() const {
    throw std::logic_error("Socket::Address type should not exist.");
}

Socket::Address::Address() {
    memset( &storage_, 0, sizeof(storage_));
}

Socket::Address 
Socket::Address::Factory( Socket::Address::sockaddr_storage_t const & storage ) {
    switch( storage.ss_family ) {
      case AF_INET:
        return InetAddress( storage );
    };
    throw std::range_error("Unrecognized address family.");
}

InetAddress::InetAddress( uint32_t addr, uint16_t port ) {
    struct sockaddr_in *s_in((struct sockaddr_in *)&storage_);
    s_in->sin_addr.s_addr = addr;
    s_in->sin_port = port;
}
